package com.ta;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName FixedDateTactics
 * @Description 通过时间控制通过策略
 * @Author 86136
 * @Date 2023/1/31 20:05
 **/
public class FixedDateTactics implements Tactics {

    //开始时间
    private final long startDate;

    //间隔时间
    private final long intervalDate;

    //列头
    private DateInfo head;

    //列尾
    private DateInfo end;

    //次数
    private final int count;

    //长度
    private final AtomicInteger size = new AtomicInteger(0);

    //队列最大长度
    private static final int LINE_MAX_SIZE = 10;

    public FixedDateTactics(long intervalDate, int count){
        this.startDate = System.currentTimeMillis();
        this.intervalDate = intervalDate;
        this.count = count;
        initHead();
    }

    private void initHead(){
        DateInfo dateInfo = new DateInfo();
        dateInfo.setKey(keyValue());
        head = dateInfo;
        end = dateInfo;
        size.incrementAndGet();
    }

    /**
     * 计算当前时间和初始时间的差值
     * @return
     */
    public long currentDiffer(){
        long c = System.currentTimeMillis();
        return c - startDate;
    }

    /**
     * 计算所在的阶段
     * @return
     */
    public long keyValue(){
        return currentDiffer()/intervalDate;
    }

    @Override
    public boolean out() {
        long currentKey = keyValue();
        if(currentKey == head.key){
            return consilientCount(head);
        } else {
            if (currentKey < head.key) {
                return consilientCount(retreat(head, currentKey));
            } else {
                DateInfo addNode = addNode(currentKey);
                return consilientCount(addNode);
            }
        }
    }

    private DateInfo retreat(DateInfo node, long currentKey){
        while (node.next != null){
            node = node.next;
            if (node.key == currentKey) {
                return node;
            }
        }
        throw new RuntimeException("请检查，错误");
    }

    //添加节点,头插法
    public DateInfo addNode(long currentKey){
        DateInfo dateInfo = new DateInfo();
        dateInfo.setKey(currentKey);
        if (head.setTypeOne()) {
            return toNode(dateInfo);
        } else {
            return tn(dateInfo);
        }
    }

    //自旋失败，重复自旋
    private DateInfo tn(DateInfo dateInfo){
        for (;;){
            if (dateInfo.key == head.key) {
                return head;
            } else if (dateInfo.key < head.key) {
                return retreat(head, dateInfo.key);
            }
            if (head.setTypeOne()) {
                return toNode(dateInfo);
            }
        }
    }

    //删除尾节点
    private void deleteTail(int size){
        if (size > LINE_MAX_SIZE) {
            if (end.setTypeTwo()) {
                doTail();
            } else {
                while (!end.setTypeTwo());
                doTail();
            }
            this.size.decrementAndGet();
        }
    }

    private DateInfo toNode(DateInfo dateInfo){
        if (dateInfo.key == head.key) {
            head.setTypeZero();
            return head;
        } else if (dateInfo.key < head.key) {
            head.setTypeZero();
            return retreat(head, dateInfo.key);
        }
        deleteTail(size.incrementAndGet());
        head.pre = dateInfo;
        dateInfo.next = head;
        head = dateInfo;
        return dateInfo;
    }

    private DateInfo doTail(){
        DateInfo pre = end.pre;
        end.pre = null;
        pre.next = null;
        end = pre;
        return pre;
    }

    private boolean consilientCount(DateInfo node){
        if (node.countIncrementAndGet() > count) {
            return false;
        }
        return true;
    }

    /**
     * 用于需要存储的数据
     */
    private class DateInfo{
        //上一个节点
        private DateInfo pre;
        //下一个节点
        private DateInfo next;
        //存储key值
        private long key;
        //存储的次数
        private final AtomicInteger count = new AtomicInteger(0);
        //状态：0：初始化好，1：有头点，2：删除
        private final AtomicInteger type = new AtomicInteger(0);

        public boolean setTypeOne() {
            return type.compareAndSet(0,1);
        }

        public boolean setTypeZero() {
            return type.compareAndSet(1,0);
        }

        public boolean setTypeTwo() {
            return type.compareAndSet(1,2);
        }

        public void setKey(long key) {
            this.key = key;
        }

        public int countIncrementAndGet() {
            return count.incrementAndGet();
        }
    }
}
